/**
 * @file magc.c
 * @brief T1 Magnetic Card Reader Driver
 *
 * @author yemt@witsi.cn
 * @date 2012-04-10
 * @version T1.UART.04
 */
#include "arch.h"
#include "magc.h"
#include "config.h"
#include "ictl.h"
#include "iodev.h"
#include <string.h>
#include <sys/ioctl.h>
#include "syscfg.h"
#include "autoconfig.h"
#include "kernel.h"
#include "running.h"

#define MAGTEK_SINGLE_TRACK_BUF_LEN 704 /* (bits) */
#define MAGTEK_BUF_LEN (3*(MAGTEK_SINGLE_TRACK_BUF_LEN)) /* (bits) */
#define MAGTEK_SN_LEN 16 /* (bits) */

#define MAG_MAX_LONG    (((MAGTEK_BUF_LEN + 31) / 32) + 1) /* +1 for sync zero */

#define STX1 0x45
#define ETX1 0x1f
#define STX2 0x0b
#define ETX2 0x1f
#define INVALID_DATA  -1
#define NOT_GET_ETX   -2
#define LRC_ERROR     -3
#define NOT_GET_STX   -4

#define MAGTEK_STROBE_GPIO      5 
#define MAGTEK_DATA_GPIO       	6

#define CFG_MAGTEK_STROBE_OUTPUT(val) do{gpio_set_output(MAGTEK_STROBE_GPIO, 0); SET_MAGTEK_STROBE_LEVEL(val);}while(0)
#define CFG_MAGTEK_STROBE_INPUT()     gpio_set_input(MAGTEK_STROBE_GPIO, 0)
#define CFG_MAGTEK_DATA_OUTPUT(val)   do{gpio_set_output(MAGTEK_DATA_GPIO, 0); SET_MAGTEK_DATA_LEVEL(val);}while(0)
#define CFG_MAGTEK_DATA_INPUT()       do{gpio_set_input(MAGTEK_DATA_GPIO, 0); MAGTEK_DATA_INT_DISABLE();}while(0)
#define CFG_MAGTEK_DATA_INT()         do{gpio_set_input(MAGTEK_DATA_GPIO, 0); MAGTEK_DATA_INT_ENABLE();}while(0)
#define MAGTEK_DATA_INT_DISABLE()     gpio_irq_disable(MAGTEK_DATA_GPIO)
#define MAGTEK_DATA_INT_ENABLE()      gpio_irq_enable(MAGTEK_DATA_GPIO)

#define SET_MAGTEK_STROBE_LEVEL(val)  gpio_output(MAGTEK_STROBE_GPIO, val)
#define SET_MAGTEK_STROBE_H()         gpio_output(MAGTEK_STROBE_GPIO, 1)
#define SET_MAGTEK_STROBE_L()         gpio_output(MAGTEK_STROBE_GPIO, 0)
#define GET_MAGTEK_STROBE()           gpio_input(MAGTEK_STROBE_GPIO)

#define SET_MAGTEK_DATA_LEVEL(val)    gpio_output(MAGTEK_DATA_GPIO, val)
#define SET_MAGTEK_DATA_H()           gpio_output(MAGTEK_DATA_GPIO, 1)
#define SET_MAGTEK_DATA_L()           gpio_output(MAGTEK_DATA_GPIO, 0)
#define GET_MAGTEK_DATA()             gpio_input(MAGTEK_DATA_GPIO)

static volatile uint32_t track_flags;

static int cardPresented = 0;
static int cardSwiped = 0;

static void magnetic_irq(void)
{
    if (cardPresented) {
        if (GET_MAGTEK_DATA() == 0) {
            cardPresented = 0;
			cardSwiped = 1;
			track_flags = 1;
			CFG_MAGTEK_DATA_INPUT(); /* disable irq and set data io input */
            return;
        }
    }

    if (GET_MAGTEK_DATA() == 0) /* card presents */ {
        udelay(1); /* delay Tsu_stbH >=20ns */
        SET_MAGTEK_STROBE_H();
        udelay(2); /* delay TstbH_CP >=1.1us */
        SET_MAGTEK_STROBE_L();
        udelay(1); /* delay TstbL */
        if (GET_MAGTEK_DATA() == 0) /* card has been swiped */ {
            cardPresented = 0;
			cardSwiped = 1;
            CFG_MAGTEK_DATA_INPUT(); /* disable irq and set data io input */            
            return;
        }
        else {
            cardPresented = 1;
            return;
        }
    }
    return;
}

static void mag_reset(void)
{
    cardPresented = 0;
    cardSwiped = 0;
	
	MAGTEK_DATA_INT_DISABLE();
    CFG_MAGTEK_STROBE_OUTPUT(1);
    CFG_MAGTEK_DATA_OUTPUT(1);
    
    SET_MAGTEK_STROBE_H();
    SET_MAGTEK_DATA_H();
    udelay(1); /* delay Tsu1_rst >=170ns */
    SET_MAGTEK_DATA_L();
    udelay(5); /* delay Tsu2_rst >=4.5us */
    SET_MAGTEK_STROBE_L();
    udelay(1); /* delay TstbL >=250ns */
    SET_MAGTEK_STROBE_H();
    udelay(1); /* delay TstbH >=250ns */
    SET_MAGTEK_STROBE_L();
    udelay(1); /* delay TstbL */
    SET_MAGTEK_DATA_H();
    udelay(1); /* delay Th_mode >=20ns */
    SET_MAGTEK_STROBE_H();
    udelay(1); /* delay TstbH2 >=12.75us */
    SET_MAGTEK_STROBE_L();
    udelay(1); /* delay TstbL */

    /* now MAGTEK is in the very low-power "OFF" state. */

    /* To arm the MAGTEK to the read cards, the flowing sequence is required. */
    SET_MAGTEK_STROBE_H();
    udelay(1); /* delay TstbH */
    SET_MAGTEK_STROBE_L();
    udelay(1); /* delay */

    CFG_MAGTEK_DATA_INT();
    MAGTEK_DATA_INT_ENABLE();

}

static void mag_stop(void)
{
}
static int mag_read_bit(void)
{
    SET_MAGTEK_STROBE_H();
    udelay(1); /* delay TstbH */
    SET_MAGTEK_STROBE_L();
    udelay(1); /* delay TstbL */
    if (GET_MAGTEK_DATA() == 0) {
        return 1;
    }
    return 0;
}

static int mag_read_bits(uint32_t *magtekSN, uint32_t *trackBuf, uint32_t trackBufLen)
{
    uint32_t bitIndex;
    
    if (trackBufLen < MAGTEK_BUF_LEN/32) {
        return -1;
    }
    
    CFG_MAGTEK_DATA_INPUT();
    memset(trackBuf, 0x00, sizeof(uint32_t)*trackBufLen); 
    for (bitIndex=0; bitIndex < MAGTEK_SN_LEN; bitIndex++) {
        if (mag_read_bit()) {
            trackBuf[bitIndex >> 5] |= (1 << (bitIndex & 31));
        }
    }
    *magtekSN = trackBuf[0];

    for (bitIndex=0; bitIndex<MAGTEK_BUF_LEN; bitIndex++) {
        if (mag_read_bit()) {
            trackBuf[bitIndex >> 5] |= (1 << (bitIndex & 31));
        }
    }

    return 0;
}

/*
** BRIEF:
**		Reversal byte
** PARAM:
**    data[in] data to reversal
** RETURN:
**	  the reversaled data
*/
static uint32_t reversal_32bit(uint32_t data)
{
	uint32_t i;
	uint32_t tmp;

	tmp = 0;
	for (i=0x80000000; i>0; i>>=1) {
		tmp >>= 1;
		if ((data&i)!=0) {
			tmp |= 0x80000000;
		}
	}
	return (tmp);
}


/* 
  * Parity(odd, b6) b5 b4 b3 b2 b1 b0
  *   return :
  *               0 success
  *               -1 failure
  */
static int track1_parity(uint32_t data)
{
    int i, parity = 0;
    
    for (i=0x01; i<0x80; i<<=1) {
        if (data&i) parity++;
    }
    
    if (parity&0x01) return 0;

    return -1;
}


/*
** BRIEF:
**		decode track1 data
** PARAM:
**    trackBuf[in] src data
**		trackOut[out] decoded data to be stored;
**		trackOutLen[in] the length of trackOut
** RETURN:
**   >0 decode success, return the length of data
**   -1 invalid data
**   -2 does not get ETX
**   -3 LRC error
**   -4 does not get STX
**   -5 parity err
*/
	
#define INVALID_DATA  -1
#define NOT_GET_ETX   -2
#define LRC_ERROR     -3
#define NOT_GET_STX   -4
#define PARITY_ERR    -5

static int track1_decode(uint32_t *trackIn, uint8_t trackInLen,\
                          uint8_t *trackOut, uint8_t trackOutLen)
{
	uint16_t indexBit;
	uint8_t  indexTrackOut;
	uint32_t tmp;
	uint8_t  i;
	uint32_t xorLrc;
	uint8_t  getETX;
	uint8_t  reverse_done = 0;
	uint16_t indexBitSTX;
    uint8_t parityErr;

//printf("\n");
repeat:

	indexBit = 0;
	indexTrackOut = 0;
	xorLrc = 0;
	getETX = 0;
	i = 0;
    parityErr = 0;

	while ( trackIn[i]== 0) { /*find logic 1 */
		i++;
		indexBit += 32;
		if (i >= trackInLen) {
			return (INVALID_DATA);
		}
	}

	tmp = trackIn[i];
	for (i=0; i<32; i++) {
		if ((tmp&0x01) != 0) {
			indexBit += i;
			break;
		}
		tmp >>= 1;
	}

	tmp = trackIn[indexBit>>5]>>(indexBit&31);
	if ((indexBit&31) > 25) {
		tmp += trackIn[(indexBit>>5) + 1]<<(32-(indexBit&31));
	}

	while ((tmp&0x7f) != STX1){ /* find out STX1 */
		indexBit++;
/*		if (indexBit >= 20) {
			goto reverse;
		}*/
		tmp = trackIn[indexBit>>5]>>(indexBit&31);
		if ((indexBit&31) > 25) {
			tmp += trackIn[(indexBit>>5) + 1]<<(32-(indexBit&31));
		}
	}

	indexBitSTX = indexBit;
	for ( ; indexBit<(trackInLen<<5); indexBit+=7) {
		tmp = trackIn[indexBit>>5]>>(indexBit&31);
		if ((indexBit&31) > 25) {
			tmp += trackIn[(indexBit>>5) + 1]<<(32-(indexBit&31));
		}

        //if (track1_parity(tmp) < 0) parityErr = 1;

		if (getETX) {
			if ((indexBit - indexBitSTX) == 14) {
				goto reverse;
			}
			//if ((tmp&0x3f) == xorLrc) {
			if ((tmp&0x3f) == (xorLrc&0x3f)) { /* ignore parity bit */			
                //if (parityErr) return PARITY_ERR;
				return (indexTrackOut);
			}
			else {
				if (!reverse_done) {
					goto reverse;
				}
				return (LRC_ERROR);
			}
		}

		if (indexTrackOut < trackOutLen) {
			trackOut[indexTrackOut++] = (unsigned char)(tmp&0x3f) + ' '; /* turn into ASCII */
		}
//printf("%c", trackOut[indexTrackOut-1]);		
		xorLrc ^= (tmp&0x3f);

		//if ((tmp&0x7f) == ETX1) {
		if ((tmp&0x3f) == (ETX1&0x3f)) { /* ignore parity bit */
			getETX = 1;
		}
	}
	if (reverse_done) {
		return (NOT_GET_ETX);
	}

reverse:
	if (!reverse_done) { /* reverse direction */
		for (i=0; i<(trackInLen>>1); i++) {
			tmp = trackIn[i];
			trackIn[i] = reversal_32bit(trackIn[trackInLen-1-i]);
			trackIn[trackInLen-1-i] = reversal_32bit(tmp);
		}
		if ((trackInLen&1) == 1) {
			tmp = trackIn[trackInLen>>1];
			trackIn[trackInLen>>1] = reversal_32bit(tmp);
		}
		reverse_done = 1;
		goto repeat;
	}
	else {
		return (NOT_GET_STX);
	}
}

/* 
  * Parity(odd, b4) b3 b2 b1 b0
  *   return :
  *               0 success
  *               -1 failure
  */
static int track23_parity(uint32_t data)
{
    int i, parity = 0;
    
    for (i=0x01; i<0x20; i<<=1) {
        if (data&i) parity++;
    }
    
    if (parity&0x01) return 0;

    return -1;
}


/*
** BRIEF:
**		decode track2 or track3 data
** PARAM:
**    trackBuf[in] src data
**		trackOut[out] decoded data to be stored
**		trackOutLen[in] the length of trackOut
** RETURN:
**   >0 decode success, return the length of data
**   -1 invalid data
**   -2 does not get ETX
**   -3 LRC error
**   -4 does not get STX
*/
static int track23_decode(uint32_t *trackIn, uint8_t trackInLen,\
                           uint8_t *trackOut, uint8_t trackOutLen)
{
	uint16_t indexBit;
	uint8_t indexTrackOut;
	uint32_t tmp;
	uint8_t i;
	uint32_t xorLrc;
	uint8_t getETX;
	uint8_t reverse_done = 0;
	uint16_t indexBitSTX;
    uint8_t parityErr;
//printf("\n");
repeat:

	indexBit = 0;
	indexTrackOut = 0;
	xorLrc = 0;
	getETX = 0;
	i = 0;
    parityErr = 0;

	while ( trackIn[i]== 0) { /* find logic 1 */
		i++;
		indexBit += 32;
		if (i >= trackInLen) {
			return (INVALID_DATA);
		}
	}

	tmp = trackIn[i];
	for (i=0; i<32; i++) {
		if ((tmp&0x01) != 0) {
			indexBit += i;
			break;
		}
		tmp >>= 1;
	}

	tmp = trackIn[indexBit>>5]>>(indexBit&31);
	if ((indexBit&31) > 27) {
		tmp += trackIn[(indexBit>>5) + 1]<<(32-(indexBit&31));
	}

	while ((tmp&0x1f) != STX2){ /* find out STX2 */
		indexBit++;
		/*if (indexBit >= 40) {
			goto reverse;
		}
		*/
		tmp = trackIn[indexBit>>5]>>(indexBit&31);
		if ((indexBit&31) > 27) {
			tmp += trackIn[(indexBit>>5) + 1]<<(32-(indexBit&31));
		}
	}

	/* forward direction */
	indexBitSTX = indexBit;
	for ( ; indexBit<(trackInLen<<5); indexBit+=5) {
		tmp = trackIn[indexBit>>5]>>(indexBit&31);
		if ((indexBit&31) > 27) {
			tmp += trackIn[(indexBit>>5) + 1]<<(32-(indexBit&31));
		}

        //if (track23_parity(tmp) < 0) parityErr = 1;

		if (getETX) {
			if ((indexBit - indexBitSTX) == 10) {
				goto reverse;
			}
			//if ((tmp&0x0f) == xorLrc) {
			//printf("LRC: 0x%02x\n", tmp);
			if ((tmp&0x0f) == (xorLrc&0x0f)) { /* ignore parity bit */
                //if (parityErr) return PARITY_ERR;
				return (indexTrackOut);
			}
			else {
				if (!reverse_done) {
					goto reverse;
				}
				return (LRC_ERROR);
			}
		}

		if (indexTrackOut < trackOutLen) {
			trackOut[indexTrackOut++] = (unsigned char)(tmp&0x0f) + '0'; /* turn into ASCII */
		}
		xorLrc ^= (tmp&0x0f);
//printf("%c", trackOut[indexTrackOut-1]);
		
		//if ((tmp&0x1f) == ETX2) {
		if ((tmp&0x0f) == (ETX2&0x0f)) { /* ignore parity bit */
			getETX = 1;
		}
	}
	if (reverse_done) {
		return (NOT_GET_ETX);
	}

reverse:
	if (!reverse_done) { /* reverse direction */
			for (i=0; i<(trackInLen>>1); i++) {
			tmp = trackIn[i];
			trackIn[i] = reversal_32bit(trackIn[trackInLen-1-i]);
			trackIn[trackInLen-1-i] = reversal_32bit(tmp);
		}
		if ((trackInLen&1) == 1) {
			tmp = trackIn[trackInLen>>1];
			trackIn[trackInLen>>1] = reversal_32bit(tmp);
		}
		reverse_done = 1;
		goto repeat;
	}
	else {
		return (NOT_GET_STX);
	}
}

/*
**	trackBuf[out] where decode data to be stored;
**		trackBuf[0]: track validate flag,
**			bit0: set 1 means that track1 decode succuss
**			bit1: set 1 means that track2 decode succuss
**			bit2: set 1 means that track3 decode succuss
**			bit4: set 1 means that track1 data presents
**			bit5: set 1 means that track2 data presents
**			bit6: set 1 means that track3 data presents
**		trackBuf[1]: valid data length amount in bytes for track1
**		trackBuf[2]: valid data length amount in bytes for track2
**		trackBuf[3]: valid data length amount in bytes for track3
**		trackBuf[4...]: valid data, stored with (track1 + track2 + track3)
**    trackBuf: trackBuf[0] + trackBuf[1] + trackBuf[2] + trackBuf[3] + '%' + track1 + '?' + ';' + track2 + '?' + ';' + track3 + '?'
**    note: the length of trackBuf >= 10
** RETURN:
**   the length of trackBuf
**
*/
#define SYNC_ZERO       32 /* leading zero bits */
#define TRACK1_OFFSET   0
#define TRACK1_BITS_LEN (704+SYNC_ZERO)
#define TRACK2_OFFSET   (TRACK1_OFFSET + TRACK1_BITS_LEN / 32 - 1) /* leading zero bits added */
#define TRACK2_BITS_LEN (704+SYNC_ZERO)
#define TRACK3_OFFSET   (TRACK2_OFFSET + TRACK2_BITS_LEN / 32 - 1) /* leading zero bits added */
#define TRACK3_BITS_LEN (704+SYNC_ZERO)

static uint32_t magc_decode(uint32_t *trackIn, uint32_t trackInLen,
                             uint8_t *trackOut, uint32_t trackOutLen)
{
	int32_t retVal;
	uint32_t index=4;
	uint8_t i;
	uint32_t bitOffset = 0;
	uint32_t tmp;
	uint32_t dataBak;
	uint32_t track1Buf[TRACK1_BITS_LEN/32 + 1];
	
	for (i=0; i<4; i++) {
		trackOut[i] = 0;
	}

	if (trackIn[TRACK1_OFFSET+1]&0xff) {
		trackOut[0] |= 0x10;
		memcpy(track1Buf, trackIn, (TRACK1_BITS_LEN/32)*4);
		retVal = track1_decode(&trackIn[TRACK1_OFFSET], TRACK1_BITS_LEN/32, trackOut+index, trackOutLen);
		if (retVal < 0) {
			memcpy(trackIn, track1Buf, (TRACK1_BITS_LEN/32)*4);
			bitOffset = 0;
			i = 0;
			while (trackIn[i]== 0) { /*find logic 1 */
				i++;
				bitOffset += 32;
				if (i >= trackInLen) break;
			}
			tmp = trackIn[i];
			for (i=0; i<32; i++) {
				if ((tmp&0x01) != 0) {
					bitOffset += i;
					break;
				}
				tmp >>= 1;
			}

			bitOffset += 14; /* for Magtek 21006540/1, it seems that bit14 bit15 decoded error, so we try to modify. */
	//printf("bitOffset:%d\n", bitOffset);		
			dataBak = trackIn[bitOffset/32];
			trackIn[bitOffset/32] &= ~(1<<(bitOffset%32));

			retVal = track1_decode(&trackIn[TRACK1_OFFSET], TRACK1_BITS_LEN/32, trackOut+index, trackOutLen);
		}

		if (retVal < 0) {
			memcpy(trackIn, track1Buf, (TRACK1_BITS_LEN/32)*4);
			
			trackIn[bitOffset/32] = dataBak;
			trackIn[bitOffset/32] &= ~(2<<(bitOffset%32));
			
			retVal = track1_decode(&trackIn[TRACK1_OFFSET], TRACK1_BITS_LEN/32, trackOut+index, trackOutLen);
		}

		if (retVal < 0) {
			memcpy(trackIn, track1Buf, (TRACK1_BITS_LEN/32)*4);
			
			trackIn[bitOffset/32] = dataBak;
			trackIn[bitOffset/32] &= ~(3<<(bitOffset%32));
			
			retVal = track1_decode(&trackIn[TRACK1_OFFSET], TRACK1_BITS_LEN/32, trackOut+index, trackOutLen);
		}

		
		if (retVal < 0) {
			trackOut[index++] = '%';
			trackOut[index++] = '?';
	//printf("track1_decode ret:%d\n", retVal);		
		}
		else {
			index += retVal;
			trackOut[0] |= 0x01;
			trackOut[1] = (uint8_t) retVal;
		}
	}
	else {
		trackOut[index++] = '%';
		trackOut[index++] = '?';
	}

	if (trackIn[TRACK2_OFFSET+1]&0xff) {
		trackOut[0] |= 0x20;
		retVal = track23_decode(&trackIn[TRACK2_OFFSET], TRACK2_BITS_LEN/32, trackOut+index, trackOutLen-index);
		if (retVal < 0) {
			trackOut[index++] = ';';
			trackOut[index++] = '?';
	//printf("track2 decode ret:%d\n", retVal);			
		}
		else {
			index += retVal;
			trackOut[0] |= 0x02;
			trackOut[2] = (uint8_t) retVal;
		}
	}
	else {
		trackOut[index++] = ';';
		trackOut[index++] = '?';
	}

	if (trackIn[TRACK3_OFFSET+1]&0xff) {
		trackOut[0] |= 0x40;
		retVal = track23_decode(&trackIn[TRACK3_OFFSET], TRACK3_BITS_LEN/32, trackOut+index, trackOutLen-index);
		if (retVal < 0) {
			trackOut[index++] = ';';
			trackOut[index++] = '?';
	//printf("track3 decode ret:%d\n", retVal);			
		}
		else {
			index += retVal;
			trackOut[0] |= 0x04;
			trackOut[3] = (uint8_t) retVal;
		}
	}
	else {
		trackOut[index++] = ';';
		trackOut[index++] = '?';
	}
	
	return index;
}

static ssize_t t1_magc_read(iodev_t *dev, void *buffer, size_t size)
{
	#define TRACK_LEN 250
	#define MAGK_READ_BUF_LEN 300
	
	unsigned char trackBuf[TRACK_LEN];
	int len;
	uint32_t SN;
	uint32_t magkReadBuff[MAG_MAX_LONG];

	if (!cardSwiped)  {
		return 0;
	}

	magkReadBuff[0] = 0; /* SYNC zero */
	mag_read_bits(&SN, &magkReadBuff[1], MAG_MAX_LONG);
    
#if 0
int i;
printf("magkReadBuff: ");
for (i=0; i<MAG_MAX_LONG; i++) {
	printf("0x%08x ", magkReadBuff[i]);
}
printf("\n");
#endif
    mag_reset();

	len = magc_decode(magkReadBuff, MAG_MAX_LONG, trackBuf, TRACK_LEN);
#if 0
        int i;
        printf("trackBuf: ");
            for (i=0; i<TRACK_LEN; i++) {
	    printf("%c ", trackBuf[i]);
        }
        printf("\n");
#endif
	if (size < len) len = size;
    memcpy(buffer, (void*)trackBuf, len);
	
	return len;
}

static int t1_magc_poll(iodev_t *dev, uint32_t flags)
{
        if (flags & POLL_READ) {
            if (!cardSwiped) {//cardSwiped
                flags &= ~POLL_READ;
            }
        }

        return flags;
}

static int t1_magc_init(iodev_t *dev)
{	
	gpio_request_irq(MAGTEK_DATA_GPIO, GPIO_INT_FALLING, magnetic_irq); 
    mag_reset();
    return 0;  
}

static int t1_magc_ioctl(iodev_t *dev, uint32_t cmd, void *arg)
{
	switch (cmd) {
		case IOCTL_MAGC_START:
            //if(cardSwiped)
      		   //mag_reset();
			break;
		case IOCTL_MAGC_STOP:
			mag_stop();
			break;
		default:
			break;
	}
	return 0;
}

static const iodev_t t1_magc __iodev_entry = {
        .name = "magc",
        .priv = NULL,
        .init = t1_magc_init,
        .read = t1_magc_read,
        .write = NULL,
        .ioctl = t1_magc_ioctl,
        .poll = t1_magc_poll,
#ifdef CONFIG_POWER_CTL
        .suspend = NULL,
        .wakeup = NULL,
#endif
};

